﻿let code = require('../constant/code');
let roomProto = require('../API/Protos/RoomProto');
let dao = require('../dao/commonDao');
let pushAPI = require('../API/pushAPI');
let logger = require('pomelo-logger').getLogger('room');
let utils = require('../util/utils');
let scheduler = require('pomelo-scheduler');
let enumeration = require('../constant/enumeration');
let userDao = require('../dao/userDao');
let userInfoServices = require('../services/userInfoServices');
let pomeloServices = require('../services/pomeloServices');
let pomelo = require('pomelo');
let gameConfig = require('./gameConfig');
let moment = require('moment');
const redisService = require('../services/redisService');
const gameDataService = require('../services/gameDataServices');
const logServices = require('../services/logServices');


module.exports = function (roomID, creatorInfo, gameRule) {
    return new roomFrame(roomID, creatorInfo, gameRule);
};

let roomFrame = function (roomID, creatorInfo, gameRule) {

    this.app = pomelo.app;
    this.publicParameter = this.app.get('config');

    // 房间基本信息
    this.roomID = roomID;
    this.gameType = gameRule.gameType;
    this.gameRule = gameRule;
    this.creatorInfo = creatorInfo;
    this.unionRoom = this.isCreateByUnion(creatorInfo); //是否是联盟创建的房间
    this.alreadyCostUserUidArr = [];
    this.payTicketUser = []; //已经支付门票的玩家
    this.readyList = [];

    // 房间状态
    this.createTime = Date.now();
    this.lastNativeTime = Date.now();
    this.gameStarted = false;
    this.roomDismissed = false;
    this.hasFinishedOneBureau = false;
    this.hasStartedOneBureau = false;
    this.payDiamondUser = [];
    // 当前局数
    this.maxBureau = gameRule.bureau || 8;
    this.curBureau = 0;

    // 房间用户信息
    this.chairCount = gameRule.maxPlayerCount || gameConfig.getDefaultMaxPlayerCount(this.gameType);
    this.currentUserCount = 0;
    this.userArr = {};
    this.offlineSchedulerIDs = {};
    this.clearUserArr = {};
    this.autoReadyTimer = {}; //玩家准备
    this.startSchedulerID = null;
    // 记录玩家从第几局加入游戏的局数
    this.userJoinGameBureau = {};
    this.userGetHongBaoCountArr = [];

    // 创建游戏逻辑
    let gameFrame = require('./' + gameConfig.getGameFrameSink(this.gameType));
    this.gameFrameSink = new gameFrame(this);

    this.askDissmissCount = {};
    this.kickSchedules = {};
    //游戏是否结束
    this.isGameEnd = false;

    //判断游戏是否可以开始
    this.isCanStart = true;
    //判断用户是否可以离开
    this.isCanLeave = true;

    this.userWinTotalScore = {};
    //2024.05.15 增加机器人金币记录
    this.userRobotTotalScore = {};
    //玩家准备
    this.isCanNotReady = {}; //是否可以准备 

};

let pro = roomFrame.prototype;

// ----------------------------------消息接收相关----------------------------------
pro.receiveRoomMessage = function (uid, msg) {


    let type = msg.type || null;
    let data = msg.data || null;
    if (!type || !data || !this.userArr[uid]) return;

    if (type === roomProto.USER_READY_NOTIFY) {
        this.userReady(uid, data);
    } else if (type === roomProto.USER_LEAVE_ROOM_NOTIFY) {
        this.userAskLeaveRoom(uid);
    } else if (type === roomProto.GET_ROOM_SCENE_INFO_NOTIFY) {
        this.getRoomSceneInfo(uid);
    } else if (type === roomProto.ASK_FOR_DISMISS_NOTIFY) {
        this.askForDismiss(uid, data.isExit).catch(err => logger.error(err.stack));
    } else if (type === roomProto.USER_CHANGE_SEAT_NOTIFY) {
        this.userChangeSeat(uid, data.fromChairID, data.toChairID);
    } else if (type === roomProto.USER_CHAT_NOTIFY) {
        this.userChat(uid, data.type, data.msg, data.recipientID);
    } else if (type === roomProto.USER_GIFT_SEND_NOTIFY) {
        this.userSendGift(uid, data.toChairID, data.giftID);
    } else if (type === roomProto.GAME_INVITATION_NOTIFY) {
        this.gameInvitation(uid, data);
    } else {
        logger.error("roomFrame", "receiveRoomMessage err: type not find");
    }
};

pro.receiveGameMessage = function (uid, msg) {
    let user = this.userArr[uid];
    if (!user) {
        logger.error("receiveGameMessage", "用户不在此房间中，uid=" + uid);
        return;
    }
    this.gameFrameSink.receivePlayerMessage(user.chairID, msg);
};

// ----------------------------------消息发送相关----------------------------------
pro.sendRoomData = function (msg, uidAndFrontendIdArr) {

    if (!uidAndFrontendIdArr) {
        uidAndFrontendIdArr = [];
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (!!user.userInfo.frontendId) {
                    uidAndFrontendIdArr.push({ uid: key, sid: user.userInfo.frontendId });
                }
            }
        }
    } else {
        let tempArr = [];
        for (let i = 0; i < uidAndFrontendIdArr.length; ++i) {
            if (!!uidAndFrontendIdArr[i].sid) {
                tempArr.push(uidAndFrontendIdArr[i]);
            }
        }
        uidAndFrontendIdArr = tempArr;
    }
    if (uidAndFrontendIdArr.length === 0) return;
    //logger.error(`${this.getLogTitle()} sendRoomData  uidAndFrontendIdArr = ${JSON.stringify(uidAndFrontendIdArr)} ]`);
    pushAPI.roomMessagePush(msg, uidAndFrontendIdArr);
};


pro.sendRoomDataToAll = function (msg) {
    this.sendRoomData(msg, null);
};

pro.sendPopDialogContent = function (code, content, chairIDArr) {
    if (!chairIDArr) {
        chairIDArr = [];
        for (let i = 0; i < this.chairCount; ++i) {
            chairIDArr.push(i);
        }
    }
    let uidAndFrontendIdArr = [];
    for (let i = 0; i < chairIDArr.length; ++i) {
        let user = this.getUserByChairID(chairIDArr[i]);
        if (!!user) {
            uidAndFrontendIdArr.push({
                uid: user.userInfo.uid,
                sid: user.userInfo.frontendId
            });
        }
    }
    if (uidAndFrontendIdArr.length === 0) return;
    logger.info('sendPopDialogContent sendData:');
    logger.info(code);
    pushAPI.popDialogContentPush({ code: code, content: content }, uidAndFrontendIdArr);
};

pro.sendPopDialogContentToAll = function (code, content) {
    this.sendPopDialogContent(code, content, null);
};

pro.sendRoomDataExceptUid = function (msg, uidArr) {
    let uidAndFrontendIdArr = [];
    let key;
    for (key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            if (uidArr.indexOf(key) === -1) {
                uidAndFrontendIdArr.push({ uid: key, sid: user.userInfo.frontendId });
            }
        }
    }
    this.sendRoomData(msg, uidAndFrontendIdArr);
};

//更新用户信息
pro.updateRoomUserInfo = function (newUserInfo, notify) {
    let user = this.userArr[newUserInfo.uid];
    if (!user) return;
    // 更新用户信息
    for (let key in newUserInfo) {
        if (newUserInfo.hasOwnProperty(key) && user.userInfo.hasOwnProperty(key) && key !== 'uid') {
            if (key == 'score') {
                user.userInfo[key] = parseFloat(newUserInfo[key].toFixed(2));
            } else {
                user.userInfo[key] = newUserInfo[key];
            }
        }
    }
    if (!!notify) this.sendRoomDataToAll(roomProto.userInfoChangePush(user.userInfo));
};



// ---------------------------------游戏开始相关----------------------------------

//玩家加入旁观
pro.userJoinWatch = async function (userInfo) {

    this.showLog(`[${userInfo.uid}] user join watch begin nickname =  ${userInfo.nickname}`);
    if (this.roomDismissed) {
        this.showLog(`[${userInfo.uid}] room is dismissed join fail`);
        return code.ROOM_JOIN_FAIL;
    }

    if (this.userArr[userInfo.uid] && this.userArr[userInfo.uid].chairID < this.chairCount) {
        this.userEntryRoom(userInfo);
        return;
    }

    let isCanWatch = this.gameRule.canWatch;
    if (isCanWatch) {
        // 将用户信息转化为对应俱乐部的信息
        if (this.creatorInfo.creatorType === enumeration.roomCreatorType.UNION_CREATE) {
            userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, this.creatorInfo.unionID);
        } else {
            userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, 1);
        }

        //房间允许旁观
        userInfo.score = parseFloat(userInfo.score.toFixed(2));
        let chairID = this.chairCount + utils.getLength(this.userArr);
        let maxchairID = this.chairCount;
        for (let key in this.userArr) {
            let id = this.userArr[key].chairID;
            if (id > maxchairID) {
                maxchairID = id;
            }
        }
        if (chairID <= maxchairID) {
            chairID = maxchairID + 1;
        }

        //保存玩家信息
        let user = { userInfo: userInfo, chairID: chairID, userStatus: roomProto.userStatusEnum.WATCH, };
        this.userArr[userInfo.uid] = user;

        // 向其他玩家推送进入房间的消息
        let roomUserInfo = { userInfo: user.userInfo, userStatus: user.userStatus, chairID: user.chairID };

        if (!userInfo.isRobot && (!userInfo.frontendId || userInfo.frontendId == '')) {
            let curUser = await userDao.getUserDataByUid(userInfo.uid);
            this.showLog(`uid = ${userInfo.uid} get new  frontendId = ${curUser && curUser.frontendId}`);
            this.userArr[userInfo.uid].userInfo.frontendId = curUser && curUser.frontendId;
        }

        this.showLog(`user join room push message uid = ${roomUserInfo.userInfo.uid} chairID = ${roomUserInfo.chairID}  userStatus = ${roomUserInfo.userStatus} `);
        let otherUserEntryRoomPush = roomProto.otherUserEntryRoomPush(roomUserInfo, this.gameType);
        this.sendRoomDataExceptUid(otherUserEntryRoomPush, [user.userInfo.uid]);

        // 推送玩家自己进入房间的消息
        if (!userInfo.isRobot) {
            let gameData = this.getGameData(user);
            let userInfoArr = this.getUserInfoArr();
            let session = await pomeloServices.getSession(userInfo.frontendId, userInfo.uid);
            if (!!session) {
                await pomeloServices.pushSessionData(session, 'roomID', this.roomID);
            } else {
                logger.error(`[${userInfo.uid}] session为空进入失败`);
                return code.FAIL;
            }
            pushAPI.selfEntryRoomPush(roomProto.selfEntryRoomPush(this.gameType, this.gameRule, this.roomID, gameData, userInfoArr, this.creatorInfo), [{
                uid: user.userInfo.uid,
                sid: user.userInfo.frontendId
            }]);
        }

        this.gameFrameSink && this.gameFrameSink.onEventUserEntry(user.chairID);

        // 更新用户信息
        let newUserData = await userDao.updateUserDataByUid(userInfo.uid, { roomID: this.roomID });
        if (!userInfo.isRobot) {
            await userInfoServices.updateUserDataNotify(newUserData.uid, newUserData.frontendId, { roomID: this.roomID }).catch(err => { });
        }

        this.showLog(`[${userInfo.uid}] user join watch success chairID = ${roomUserInfo.chairID} playStatus = ${roomUserInfo.playStatus} userStatus = ${roomUserInfo.userStatus}`);
        return code.OK;

    } else {
        this.showLog(`[${userInfo.uid}] room not allow watch`, true);
        return code.ROOM_NOT_WATCH;
    }

}

pro.userReady = function (uid) {

    this.showLog(`ask ready uid = ${uid}  gameStarted = ${this.gameStarted}  roomDismissed = ${this.roomDismissed} `);
    if (this.gameStarted || this.roomDismissed) return;
    let user = this.userArr[uid];
    if (!user) return;

    //如果在离开状态 不能退出
    if (this.isCanNotReady[uid]) {
        this.showLog(`[${uid}] user ready fail can not ready kick auto`, true);
        return;
    }

    if (!!user && user.userStatus == roomProto.userStatusEnum.WATCH) {
        this.showLog(`watch user can not ready uid = ${uid} userStatus = ${user.userStatus}`, true);
        return;
    } else if (!!user && (user.userStatus & roomProto.userStatusEnum.READY) == 0) {

        if (this.autoReadyTimer[uid]) {
            this.showLog(`[${uid}] user ready and close ready timer`);
            clearTimeout(this.autoReadyTimer[uid]);
            delete this.autoReadyTimer[uid];
        }

        this.showLog(`update user status uid = ${uid} userStatus = ${this.userArr[uid].userStatus} change to = ${roomProto.userStatusEnum.READY}`);
        this.userArr[uid].userStatus = roomProto.userStatusEnum.READY;
        this.sendRoomData(roomProto.userReadyPush(user.chairID));

    }

    if (!this.readyList.includes(uid)) {
        this.readyList.push(uid);
    }

    // 判断游戏是否需要开始
    let isEfficacyStartGame = this.efficacyStartGame();
    if (isEfficacyStartGame && this.isCanStart) {
        //游戏开始不能离开了
        this.isCanLeave = false;
        this.showLog(`game started user can not leave isCanLeave = ${this.isCanLeave} `);
        this.startGame().catch((err) => { logger.error(err.stack); });
        return;
    } else {
        this.showLog(`game can not start startGame = ${isEfficacyStartGame} isCanStart = ${this.isCanStart}`);
        return;
    }

};


pro.isShouldSchedulerStart = function () {
    if (this.gameStarted) return false;
    if (this.hasStartedOneBureau) return false;
    if (this.roomDismissed) return false;
    let readyCount = 0;
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            if (user.chairID < this.chairCount) { // 大于10的玩家未入座
                if ((this.userArr[key].userStatus & roomProto.userStatusEnum.READY) > 0) ++readyCount;
            }
        }
    }
    return (readyCount >= 4 && readyCount >= this.gameRule.minPlayerCount);
};


pro.startGame = async function () {

    this.showLog(`game start begin gameStarted = ${this.gameStarted}  gameEnd = ${this.isGameEnd}`);
    if (this.gameStarted) return;
    this.gameStarted = true;
    if (!!this.startSchedulerID) {
        clearInterval(this.startSchedulerID);
        this.startSchedulerID = null;
    }
    for (let key in this.kickSchedules) {
        clearTimeout(this.kickSchedules[key]);
        delete this.kickSchedules[key];
    }

    // 第一局游戏开局时收取房费
    if (this.maxBureau > 0) {
        // 判断联盟是否已经解散
        if (this.curBureau === 0 && this.creatorInfo.creatorType === enumeration.roomCreatorType.UNION_CREATE) {
            let union = await pomelo.app.unionManager.getUnion(this.creatorInfo.unionID);
            if (!union.isOpening()) {
                this.sendPopDialogContentToAll(null, "联盟已打烊，无法开始新的牌局");
                await this.dismissRoom(enumeration.gameRoomDismissReason.UNION_OWNER_DISMISS);
                this.gameStarted = false;
                return;
            }
        }
        if (this.curBureau == 0) {

            if (this.gameRule.payType == enumeration.roomPayType.WOZHIFU) {
                let payResult = await this.payRoomCost();
                if (payResult != code.OK) {
                    // 收取房费失败，解散房间
                    this.sendPopDialogContentToAll(null, "扣取房费失败，房间已解散");
                    await this.dismissRoom(enumeration.gameRoomDismissReason.UNION_OWNER_DISMISS);
                    this.gameStarted = false;
                    return;
                }
            }
        }

        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                if (this.userArr[key].chairID >= this.chairCount) continue;
                this.userJoinGameBureau[key] = (this.userJoinGameBureau[key] || 0) + 1 // 记录加入游戏的局数
            }
        }
    }


    this.hasStartedOneBureau = true;
    this.lastNativeTime = Date.now();
    // 更新参数
    this.publicParameter = this.app.get('config');


    //如果是AA支付 每一局都要扣钱
    if (this.gameRule.payType === enumeration.roomPayType.AAZHIFU) {
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (!this.payDiamondUser.includes(user.userInfo.uid) && user.chairID < this.chairCount && user.userStatus != roomProto.userStatusEnum.WATCH) {
                    await this.userPayCost(user.userInfo.uid);
                    this.payDiamondUser.push(user.userInfo.uid);
                }
            }
        }
    }
    this.isGameEnd = false;
    this.showLog(`game start over gameStarted = ${this.gameStarted}  gameEnd = ${this.isGameEnd}`);
    this.gameFrameSink.onEventGameStart();
};

// 判定游戏是否可以开始
pro.efficacyStartGame = function () {

    if (this.roomDismissed || this.gameStarted) return false;
    let readyCount = 0;
    let userCount = 0;
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            if (user.chairID < this.chairCount && user.userStatus != roomProto.userStatusEnum.WATCH) {
                userCount += 1;
                if ((this.userArr[key].userStatus & roomProto.userStatusEnum.READY) > 0) {
                    readyCount += 1;
                };
            }
        }
    }
    this.showLog(`check game start userCount = ${userCount} readyCount = ${readyCount} minPlayerCount = ${this.gameRule.minPlayerCount} maxPlayerCount = ${this.gameRule.maxPlayerCount}`);

    if (gameConfig.isFullStart(this.gameType)) {
        return (userCount === readyCount && userCount >= this.gameRule.minPlayerCount) || readyCount === this.gameRule.maxPlayerCount;
    } else {
        //多人游戏2人准备后开始
        if (readyCount >= 2) {
            for (let key in this.userArr) {
                if (this.userArr.hasOwnProperty(key)) {
                    let user = this.userArr[key];
                    if (user.chairID < this.chairCount && user.userStatus != roomProto.userStatusEnum.WATCH && user.userStatus != roomProto.userStatusEnum.READY) {
                        this.userArr[key].userStatus = roomProto.userStatusEnum.READY;
                        //推送用户准备消息
                        this.sendRoomData(roomProto.userReadyPush(user.chairID));
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }
};


//检测砖石是否足够
pro.checkPayDiamond = async function () {

    if (this.gameRule.payType == enumeration.roomPayType.AAZHIFU) {
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (user.chairID >= this.chairCount || user.playStatus == roomProto.userPlayEnum.WATCH) continue;
                let curUser = await userDao.getUserDataByUid(user.userInfo.uid);
                let payDiamond = (this.gameRule.perDiamond || this.gameRule.payDiamond) || 1;
                if (curUser.gold < payDiamond) {
                    this.showLog(`[${user.userInfo.uid}] have no gold userGold = ${curUser.gold}  payDiamond = ${payDiamond}  must be kick`);
                    await this.kickUser(user.userInfo.uid);
                }
            }
        }
    }
}

//支付房费
pro.payRoomCost = async function () {

    if (this.creatorInfo.creatorType == enumeration.roomCreatorType.UNION_CREATE) {

        if (this.curBureau == 0) {
            let union = await pomelo.app.unionManager.getUnion(this.creatorInfo.unionID);
            let unionOwnerUid = union.getOwnerUid();
            return await gameDataService.unionRoomPay(unionOwnerUid, this.gameRule, this.roomID, this.creatorInfo.unionID, this.gameType);
        } else {
            return code.OK;
        }
    } else {
        //普通场支付
        if (this.curBureau == 0) {
            return gameDataService.roomPay(this.userArr, this.gameRule, this.chairCount, this.roomID, this.creatorInfo.uid, this.gameType);
        }
        return code.OK;
    }
}

//支付
pro.userPayCost = async function (uid) {

    let payDiamond = (this.gameRule.perDiamond || this.gameRule.payDiamond) || 1;
    return await gameDataService.userPay(uid, payDiamond, this.roomID, this.creatorInfo.unionID || -1, this.gameType);
}


// ---------------------------------游戏结束相关----------------------------------
pro.concludeGame = async function (data) {

    this.showLog(`game end save game records = ${JSON.stringify(data)}`);
    if (!this.gameStarted) return;

    // 修改玩家状态
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            user.userStatus = roomProto.userStatusEnum.NONE;
        }
    }

    //推送总输赢
    for (let i = 0; i < data.length; i++) {
        let uid = data[i].uid;
        let chairID = this.userArr[uid].chairID;
        let score = data[i].score;
        this.userWinTotalScore[chairID] = (this.userWinTotalScore[chairID] || 0) + score;
    }
    this.sendRoomDataToAll(roomProto.roomUserWinTotalPush(this.userWinTotalScore));

    // 记录游戏结果
    await this.recordGameResult(data).catch((err) => { logger.error(err.stack); });



    // 记录已经付房费的玩家，防止重复
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            if (this.userArr[key].chairID >= this.chairCount) continue;
            if (this.alreadyCostUserUidArr.indexOf(this.userArr[key].userInfo.uid) !== -1) continue;
            this.alreadyCostUserUidArr.push(this.userArr[key].userInfo.uid);

        }
    }

    // 判断房间是否应该解散
    this.gameStarted = false;
    this.hasFinishedOneBureau = true;

    if (this.maxBureau > 0 && this.curBureau >= this.maxBureau) {

        this.showLog(`game all end begin dissmiss room curBureau = ${this.curBureau} maxBureau =${this.maxBureau} `);
        await this.dismissRoom(enumeration.gameRoomDismissReason.BUREAU_FINISHED);
    } else {

        this.showLog(`cur bureau end go to next curBureau = ${this.curBureau} maxBureau = ${this.maxBureau} `);
        //await this.checkPayDiamond();
        // 通知更新所有玩家信息
        this.notifyUpdateAllUserInfo();
    }

};

pro.notifyUpdateAllUserInfo = function () {

    // 更新所有玩家正在玩的玩家的分数变化
    for (let key in this.userArr) {
        let user = this.userArr[key];
        if (!user) continue;
        if (user.chairID >= this.chairCount) continue;
        this.sendRoomDataToAll(roomProto.userInfoChangePush(user.userInfo));
    }
};

//记录用户输赢
pro.recordGameResult = async function (dataArr) {

    if (!dataArr || dataArr.length === 0) return;
    // 计算最终获得金币数量
    for (let i = 0; i < dataArr.length; ++i) {
        let data = dataArr[i];
        let user = this.userArr[data.uid];
        let score = data.score || 0;
        let beforScore = user.userInfo.score;
        let totalScore = parseInt(user.userInfo.score + score);
        user.userInfo.score = totalScore;
        user.winScore = (user.winScore || 0) + score;
        this.showLog(`[${data.uid}] game end score change beforScore = ${beforScore} score = ${score} endScore = ${totalScore}`);
        this.updateRoomUserInfo({ uid: data.uid, score: totalScore });

        //判断玩家黑白名单
        let unionID = this.unionRoom ? this.creatorInfo.unionID : 1;
        let userType = await userInfoServices.checkUserAward(data.uid, totalScore, score, beforScore, this.gameType, unionID, this.userArr[data.uid].userInfo.userType);
        if (userType >= 0) {
            this.userArr[data.uid].userInfo.userType = userType;
        }

    }

};


pro.writeUserGameResult = async function (dataArr) {
    this.recordGameResult(dataArr).catch(err => { logger.error(err.stack); });
};


// 记录总局结果
pro.recordAllDrawResult = async function () {

    if (!this.hasFinishedOneBureau) return;
    // 记录录像
    let gameVideoRecord = null;
    let gameVideoData = this.gameFrameSink.getGameVideoData();
    if (!!gameVideoData) {
        let saveData = {
            roomID: this.roomID,
            gameType: this.gameType,
            detail: JSON.stringify(gameVideoData),
            createTime: Date.now()
        };
        gameVideoRecord = await dao.createData("gameVideoRecordModel", saveData);
    }
    // 记录游戏数据
    let userList = [];
    let userCount = 0;
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            if (this.alreadyCostUserUidArr.indexOf(key) === -1) continue;
            let userScore = parseFloat(user.winScore) || 0;
            userList.push({
                uid: key,
                nickname: user.userInfo.nickname,
                avatar: user.userInfo.avatar,
                score: userScore,
                spreaderID: user.userInfo.spreaderID
            })

            //保存玩家每次输赢和战绩匹配
            if (this.unionRoom) {
                let dateTime = moment().format('YYYY-MM-DD');
                let dayTime = moment().format('YYYYMMDD');
                let saveTeamData = { $inc: { "score": userScore, "count": 1 }, dayTime: dayTime };
                await dao.findOneAndUpdateEx('userScoreDataModel', { uid: key, unionID: this.creatorInfo.unionID, dateTime }, saveTeamData, { upsert: true, new: true });
            }

        }
    }

    //玩家在房间并且继续游戏了 不改变分数
    for (let key in this.clearUserArr) {
        if (this.clearUserArr.hasOwnProperty(key)) {
            let temp = userList.find(function (ele) {
                return ele.uid === key;
            });
            if (!temp) {
                userList.push(this.clearUserArr[key]);
            }
        }
    }


    let detail = JSON.stringify(this.gameFrameSink.getGameBureauData() || []);
    let saveData = {
        roomID: this.roomID,
        gameType: this.gameType,
        userList: userList,
        detail: detail,
        createTime: Date.now()
    };

    if (!!gameVideoRecord) {
        saveData.videoRecordID = gameVideoRecord.videoRecordID;
    }
    if (this.creatorInfo.creatorType === enumeration.roomCreatorType.UNION_CREATE) {
        saveData.unionID = this.creatorInfo.unionID;
    } else {
        saveData.creatorUid = this.creatorInfo.uid
    }
    await dao.createData("userGameRecordModel", saveData).catch(e => { logger.error(e.stack) });

};

//清除掉线玩家
pro.clearOfflineUser = async function () {
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            if (user && user.userStatus == roomProto.userStatusEnum.OFFLINE) {
                await this.kickUser(user.userInfo.uid);
            }
        }
    }
};



// ---------------------------------进入房间相关----------------------------------
/*
 * 玩家邀请
 */
pro.gameInvitation = async function (uid, data) {
    let lockKey = uid + '-Join-Room';
    let isLocked = redisService.isLocked(lockKey, 10);
    if (!isLocked) {
        let nextunionID = data.unionID; //当前玩家联盟ID
        let nextroomID = data.roomID; //房间ID

        let user = this.userArr[uid].userInfo;
        //组合邀请人数据
        let inviter = {
            uid: user.uid,
            avatar: user.avatar,
            nickname: utils.getNickName(user.nickname),
            roomID: user.roomID,
            frontendId: user.frontendId,
        };
        for (let key in data.uid) {
            let nextuid = data.uid[key]; //收到的玩家UID
            let unionData = await dao.findOneData('userModel', {
                'uid': nextuid
            });
            //玩家不在线
            if (unionData.frontendId == '') {
                await this.sendRoomData(roomProto.gameInvitationResult("玩家不在线"), [{
                    uid: user.uid,
                    sid: user.frontendId,
                }]);
                return;
            }
            //玩家已开始游戏
            if (unionData.roomID != '') {
                await this.sendRoomData(roomProto.gameInvitationResult("玩家已开始游戏"), [{
                    uid: user.uid,
                    sid: user.frontendId,
                }]);
                return;
            }

            await pushAPI.gameInvitation({
                nextunionID,
                // roomData,
                // roomID,
                nextroomID,
                inviter,
                gameRule: this.gameRule,
            }, [{
                uid: nextuid,
                sid: unionData.frontendId
            }]);
        }
        let message = roomProto.gameInvitationResult("发送邀请成功");
        await this.sendRoomData(message, [{
            uid: user.uid,
            sid: user.frontendId,
        }]);
        return;
        // this.sendRoomData(roomProto.gameInvitationPush(data.unionID,uid, data.roomID),[data.uid]);
    }
};


pro.userEntryRoom = async function (userInfo) {


    this.showLog(`userEntryRoom check begin uid = ${userInfo.uid}  name = ${userInfo.nickname}`);

    if (!this.gameFrameSink) {
        this.showLog(`gameFrameSink is null uid = ${userInfo.uid}  name = ${userInfo.nickname}`, true);
        return code.ROOM_NOT_EXIST;
    }

    if (this.roomDismissed) {
        this.showLog(`room dismissed uid = ${userInfo.uid}  name = ${userInfo.nickname}`, true);
        //清除玩家的房间号并将用户踢出房间
        let session = await pomeloServices.getSession(userInfo.frontendId, userInfo.uid);
        if (!!session) {
            await pomeloServices.pushSessionData(session, 'roomID', null);
            this.showLog(`clear user roomID  uid = ${userInfo.uid}  name = ${userInfo.nickname}`, true);
        }
        this.kickUser(userInfo.uid);
        return code.NOT_IN_ROOM;
    }

    let lockKey = this.roomID + '-Join-Room';
    let isLocked = redisService.isLocked(lockKey, 5);

    //未锁定
    if (!isLocked) {

        let user = this.userArr[userInfo.uid];
        if (!user && !this.canEnter()) {
            this.showLog(`canEnter = false room busy`, true);
            redisService.unLock(lockKey);
            return code.ROOM_PLAYER_COUNT_FULL;
        }

        // 将用户信息转化为对应俱乐部的信息
        if (this.creatorInfo.creatorType === enumeration.roomCreatorType.UNION_CREATE) {
            userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, this.creatorInfo.unionID);
        } else {
            userInfo = userInfoServices.buildGameRoomUserInfoWithUnion(userInfo, 1);
        }
        //获取座位号
        let chairID = this.getEmptyChairId(userInfo.uid, userInfo.score < this.gameRule.scoreLowLimit);
        this.showLog(`[${userInfo.uid}] get user chairID = ${chairID}`, false);
        if (this.gameRule.canWatch && this.curBureau > 0) {
            //允许旁观时座位号更改为旁观座位号
            let watchCount = this.getWatchUserCount();
            chairID += (this.chairCount + watchCount);

        } else if (chairID >= this.chairCount && !this.gameRule.canWatch) {

            this.showLog(`room user full(can not watch) chairID = ${chairID} chairCount = ${this.chairCount} isCanEnter = ${this.gameRule.canEnter} isCanWtach = ${this.gameRule.canWatch} `, true);
            redisService.unLock(lockKey);
            return code.ROOM_PLAYER_COUNT_FULL;
        }


        if (!user) {
            // 非断线重连用户需检查是否有满足进入房间的条件
            let errCode = await this.checkEntryRoom(userInfo);
            //获取最新的chairID
            chairID = this.getEmptyChairId(userInfo.uid, userInfo.score < this.gameRule.scoreLowLimit);
            this.showLog(`check user join room uid = ${userInfo.uid} chairID = ${chairID}  errCode = ${errCode} canWatch = ${this.gameRule.canWatch}`);

            if (!!errCode) {
                //不能游戏的玩家只能变为旁观 机器人不旁观
                if (this.gameRule.canWatch && !userInfo.isRobot) {
                    let watchCount = this.getWatchUserCount();
                    chairID += (this.chairCount + watchCount);
                } else {
                    redisService.unLock(lockKey);
                    return errCode;
                }
            }
            // 构建用户信息
            user = {};
            //修改用户分数
            userInfo.score = parseFloat(userInfo.score.toFixed(1));
            user.userInfo = userInfo;
            user.chairID = chairID;
            //座位号大于座位数玩家为旁观
            user.userStatus = chairID >= this.chairCount ? roomProto.userStatusEnum.WATCH : roomProto.userStatusEnum.NONE;
            this.userArr[userInfo.uid] = user;
            this.currentUserCount++;
            this.showLog(`user join room uid = ${userInfo.uid} chairID = ${user.chairID} `);
            // 更新用户信息
            let newUserData = await userDao.updateUserDataByUid(userInfo.uid, {
                roomID: this.roomID
            });
            if (!userInfo.isRobot) {
                await userInfoServices.updateUserDataNotify(newUserData.uid, newUserData.frontendId, { roomID: this.roomID }).catch(err => { });
            }

        } else {

            this.showLog(`offline reconncet  uid = ${userInfo.uid} chairID = ${user.chairID}`);
            // 更新用户信息
            user.userInfo = userInfo;
            //没有座位号当前为旁观状态
            if (user.chairID >= this.chairCount) {
                //没有座位的旁观
                this.userArr[userInfo.uid].userStatus = roomProto.userStatusEnum.WATCH;
            } else if (user.userStatus == roomProto.userStatusEnum.OFFLINE) {
                //离线变在线
                this.userArr[userInfo.uid].userStatus = roomProto.userStatusEnum.PLAYING;
            }
            // 取消离线倒计时
            if (!!this.offlineSchedulerIDs[userInfo.uid]) {
                scheduler.cancelJob(this.offlineSchedulerIDs[userInfo.uid]);
                delete this.offlineSchedulerIDs[userInfo.uid];
            }
        }
        // 修改房间用户session信息
        if (!userInfo.isRobot) {
            let session = await pomeloServices.getSession(userInfo.frontendId, userInfo.uid);
            if (!!session) {
                await pomeloServices.pushSessionData(session, 'roomID', this.roomID);
            } else {
                logger.error(`[${userInfo.uid}] session为空进入失败`);
                redisService.unLock(lockKey);
                return code.FAIL;
            }
        }

        // 向其他玩家推送进入房间的消息
        let roomUserInfo = {
            userInfo: user.userInfo,
            userStatus: user.userStatus,
            chairID: user.chairID
        };

        if (!userInfo.isRobot && (!userInfo.frontendId || userInfo.frontendId == '')) {
            let curUser = await userDao.getUserDataByUid(userInfo.uid);
            this.showLog(`uid = ${userInfo.uid} get new frontendId = ${curUser && curUser.frontendId}`);
            this.userArr[userInfo.uid].userInfo.frontendId = curUser && curUser.frontendId;
        }

        this.showLog(`push user join room message uid = ${roomUserInfo.userInfo.uid} chairID = ${roomUserInfo.chairID}  userStatus = ${roomUserInfo.userStatus} `);
        let otherUserEntryRoomPush = roomProto.otherUserEntryRoomPush(roomUserInfo, this.gameType);
        this.sendRoomDataExceptUid(otherUserEntryRoomPush, [user.userInfo.uid]);
        // 推送玩家自己进入房间的消息
        if (!userInfo.isRobot) {
            let gameData = this.getGameData(user);
            let userInfoArr = this.getUserInfoArr();
            pushAPI.selfEntryRoomPush(roomProto.selfEntryRoomPush(this.gameType, this.gameRule, this.roomID, gameData, userInfoArr, this.creatorInfo), [{
                uid: user.userInfo.uid,
                sid: user.userInfo.frontendId
            }]);
        }

        this.gameFrameSink && this.gameFrameSink.onEventUserEntry(user.chairID);
        if (user.userInfo.isRobot) {
            this.addKickScheduleEvent(user.userInfo.uid, 160);
        }


        if (this.unionRoom) {
            let unionData = await dao.findOneData('unionModel', {
                unionID: this.creatorInfo.unionID
            });
            if (unionData.unReadyKick) {
                this.gameRule.unReadyKick = unionData.unReadyKick;
            }
        }


        if (this.gameRule.unReadyKick) {
            this.addReadyKickEvent(user.userInfo.uid, this.gameRule.unReadyKick);
        }

        if (this.kickSchedules[user.userInfo.uid]) {
            clearTimeout(this.kickSchedules[user.userInfo.uid]);
            delete this.kickSchedules[user.userInfo.uid];
        }
        redisService.unLock(lockKey);
        return code.OK;

    } else {

        this.showLog(`room join locked = ${isLocked} uid = ${userInfo.uid} name = ${userInfo.nickname} `);
        return code.ROOM_JOIN_BUSY_LIMIT;
    }

};

/*
 * 能否进入游戏
 */
pro.canEnter = function () {
    let hasEmpty = this.hasEmptyChair();
    let canWatch = !!this.gameRule.canWatch;
    let canEnter = !!this.gameRule.canEnter && (this.gameType !== enumeration.gameType.PDK);
    if (this.hasStartedOneBureau) {
        if (canWatch && this.currentUserCount < 20) {
            return true;
        } else {
            return (canEnter && (hasEmpty || (canWatch && this.currentUserCount < 20)));
        }

    } else {
        return hasEmpty || (canWatch && this.currentUserCount < 20);
    }
};

pro.checkEntryRoom = async function (userInfo) {


    logger = logServices.getLogByType(this.gameType);
    // this.showLog(`[${userInfo.uid}][checkEntryRoom] 开始`);
    // 普通房间
    if (this.creatorInfo.creatorType === enumeration.roomCreatorType.USER_CREATE) {
        // 检查钻石是否足够
        if (this.gameRule.payType === enumeration.roomPayType.WOZHIFU) {
            if (userInfo.uid === this.creatorInfo.uid) {
                let payDiamond = gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
                if (userInfo.gold < payDiamond) {
                    return code.NOT_ENOUGH_GOLD;
                }
            }
        } else {
            let payDiamond = (this.gameRule.perDiamond || this.gameRule.payDiamond) || 1;
            if (userInfo.gold < payDiamond) {
                return code.NOT_ENOUGH_GOLD;
            }

        }
    }
    // 俱乐部房间
    else {

        //AA支付砖石不足
        if (this.gameRule.payType === enumeration.roomPayType.AAZHIFU) {
            let payDiamond = (this.gameRule.perDiamond || this.gameRule.payDiamond) || 1;
            if (userInfo.gold < payDiamond * this.gameRule.bureau) {
                return code.NOT_ENOUGH_GOLD;
            }
        }

        //机器人不能进入人满的房间
        if (userInfo.isRobot) {
            let count = utils.getLength(this.userArr);
            if (count > this.gameRule.maxPlayerCount + 1) {
                return code.ROOM_PLAYER_COUNT_FULL;
            }
        }

        // 检测是否被禁止加入游戏
        if (!!userInfo.prohibitGame) {
            logger.error(`[${userInfo.uid}][checkEntryRoom] REQUEST_DATA_ERROR`);
            return code.PERMISSION_NOT_ENOUGH;
        }

        let isUserInRoom = this.userArr && this.userArr[userInfo.uid];
        //如果用户在房间 或者允许观战 不受条件限制
        if (!isUserInRoom) {
            if (this.gameRule.lastThreeJoin && (this.curBureau > 0) && (this.gameRule.bureau > 3 && this.gameRule.bureau - this.curBureau < 3)) {
                logger.error(`[${userInfo.uid}][checkEntryRoom] THE_FINAL_THREE_MATCHES`);
                return code.THE_FINAL_THREE_MATCHES;
            }
        }
    }

    // 防作弊模式，计算玩家距离，任意玩家距离差不能低于100米
    if (!!this.gameRule.fangzuobi) {
        if (!userInfo.address || userInfo.address.length === 0) {
            logger.error(`[${userInfo.uid}][checkEntryRoom] CAN_NOT_ENTER_NOT_LOCATION`);
            return code.CAN_NOT_ENTER_NOT_LOCATION;
        }
        let location = JSON.parse(userInfo.address);
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                if (userInfo.uid != key) {

                    let distanceLimit = (parseInt(this.gameRule.distanceLimit) / 1000);
                    if (utils.getDistanceByLocation(location, JSON.parse(this.userArr[key].userInfo.address)) < distanceLimit) {
                        logger.error(`[${userInfo.uid}][checkEntryRoom] CAN_NOT_ENTER_TOO_NEAR`);
                        return code.CAN_NOT_ENTER_TOO_NEAR;
                    }
                    if (userInfo.lastLoginIP == this.userArr[key].userInfo.lastLoginIP) {
                        return code.CAN_NOT_ENTER_TOO_NEAR;
                    }
                }
            }
        }
    }

    this.showLog(`userEntryRoom check end`);
    return code.OK;

};

pro.sleep = function (ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

pro.getWatchUserCount = function (uid) {

    let watchCount = 0;
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            if (this.userArr[key].chairID >= this.chairCount) {
                watchCount += 1;
            }
        }
    }
    return watchCount;

}

//获取空ID
pro.getEmptyChairId = function (uid, isWatch) {


    if (this.userArr[uid]) {
        return this.userArr[uid].chairID;
    }

    let usedArr = [];
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            usedArr.push(this.userArr[key].chairID);
        }
    }
    let chairID = 0;
    while (usedArr.indexOf(chairID) !== -1) {
        ++chairID;
    }
    return chairID;

};

pro.hasEmptyChair = function () {
    let seatCount = 0;
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            if (this.userArr[key].chairID === -1) continue;
            seatCount++;
        }
    }
    return seatCount < this.chairCount;
};

pro.getRoomSceneInfo = async function (uid) {


    this.showLog(`uid = ${uid} get room scene info begin`);
    let user = this.userArr[uid];
    if (this.roomDismissed) {
        //房间已经解散
        await this.userLeaveRoom(uid).catch((err) => {
            logger.error(new Error(err.stack));
        });
        return;
    }

    if (!this.gameFrameSink) {
        await this.userLeaveRoom(uid).catch((err) => {
            logger.error(new Error(err.stack));
        });
        return;
    }

    if (!user) {
        return;
    }

    this.isGameEnd = false;


    if (this.kickSchedules[uid]) {
        clearTimeout(this.kickSchedules[uid]);
        delete this.kickSchedules[uid];
    }

    if (!user.userInfo.isRobot && (!user.userInfo.frontendId || user.userInfo.frontendId == '')) {
        let curUser = await userDao.getUserDataByUid(uid);
        this.showLog(`uid = ${uid} get new frontendId = ${curUser.frontendId}`);
        this.userArr[uid].userInfo.frontendId = curUser && curUser.frontendId;
    }

    if (this.unionRoom && this.isBiSai && user.chairID < this.chairCount && user.userInfo.score <= this.gameRule.scoreDismissLimit) {
        //在座位号上 分数不足的旁观
        user.userStatus = roomProto.userStatusEnum.WATCH;
        this.showLog(`[${uid}] get room scene limit score and watch`);
    }

    //游戏没开始需要重新准备
    if (user.chairID < this.chairCount && !this.gameStarted && this.curBureau == 0) {
        //游戏没有开始重置为未准备状态
        user.userStatus = roomProto.userStatusEnum.NONE;
        this.showLog(`[${uid}] get room scene reset to none`);
    }


    let userInfoArr = this.getUserInfoArr();
    let gameData = this.getGameData(user);
    //this.showLog(`uid = ${uid} sid = ${user.userInfo.frontendId} `);
    let min = roomProto.getRoomSceneInfoPush(this.roomID, this.creatorInfo, this.gameRule, userInfoArr, gameData);
    this.sendRoomData(min,
        [{
            uid: uid,
            sid: user.userInfo.frontendId
        }]);
    if (this.askForExitArr) {
        this.askForDismiss(uid, null).catch(err => {
            logger.error(err.stack)
        });
    }


    //this.showLog(`uid = ${user.userInfo.uid} join room chairID = ${user.chairID}  userStatus = ${user.userStatus} `);
    let otherUserEntryRoomPush = roomProto.otherUserEntryRoomPush(user, this.gameType);
    this.sendRoomDataExceptUid(otherUserEntryRoomPush, [user.userInfo.uid]);

    this.showLog(`uid = ${uid} get room scene info end`);
};

//获取游戏数据
pro.getGameData = function (user) {

    let gameData = this.gameFrameSink.getEnterGameData(user.chairID);
    gameData.userWinTotalScore = this.userWinTotalScore;
    gameData.gameRule = this.gameRule;
    return gameData;
}

//获取玩家对象
pro.getUserInfoArr = function () {

    let userInfoArr = [];
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user1 = this.userArr[key];
            if (user1) {
                user1.userInfo.score = user1.userInfo && user1.userInfo.score && parseFloat(user1.userInfo.score.toFixed(2));
                userInfoArr.push({
                    userInfo: user1.userInfo,
                    chairID: user1.chairID,
                    userStatus: user1.userStatus
                });
            }
        }
    }
    return userInfoArr;
}


// ---------------------------------解散房间相关----------------------------------
/*
 * 玩家请求解散房间
 */
pro.askForDismiss = async function (uid, isExit) {
    this.showLog(`uid = ${uid} -----------askForDismiss begin-----------------`);
    if ([true, false, null].indexOf(isExit) === -1) {
        return;
    }


    let askUser = this.userArr[uid];
    if (askUser.chairID >= this.chairCount) {
        this.userAskLeaveRoom(uid);
        return;
    }
    if (!this.askForExitArr && isExit === true) {

        //判断解散次数
        if (this.unionRoom) {

            let unionData = await dao.findOneData('unionModel', {
                unionID: this.creatorInfo.unionID
            });
            if (unionData && unionData.disApplyCount) {
                this.gameRule.disApplyCount = unionData.disApplyCount;
            }
        }

        if (this.gameRule.disApplyCount) {

            if (this.askDissmissCount[uid] && this.askDissmissCount[uid] >= this.gameRule.disApplyCount) {
                return;
            } else {
                this.askDissmissCount[uid] = (this.askDissmissCount[uid] || 0) + 1;
            }
        }

        this.askForExitArr = [];
        for (let i = 0; i < this.chairCount; ++i) {
            this.askForExitArr[i] = null;
        }

        this.dismissTick = roomProto.EXIT_WAIT_SECOND;
        this.answerExitSchedule = setInterval(() => {
            --this.dismissTick;
            if (this.dismissTick === 0) {
                if (this.answerExitSchedule) {
                    clearInterval(this.answerExitSchedule);
                    this.answerExitSchedule = null;
                }
                for (let key in this.userArr) {
                    if (this.userArr.hasOwnProperty(key)) {
                        let user = this.userArr[key];
                        if (user && user.userStatus != roomProto.userStatusEnum.OFFLINE && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
                            if (this.askForExitArr[user.chairID] == null)
                                this.askForDismiss(user.userInfo.uid, true).catch(e => {
                                    logger.error(e.stack)
                                });
                        }
                    }
                }
            }

        }, 1000);
    }

    if (!this.askForExitArr) {
        return;
    }
    if (this.askForExitArr[askUser.chairID] != null && isExit != null) {
        return;
    }
    if (isExit == true || isExit == false) {
        this.askForExitArr[askUser.chairID] = isExit;
    }

    let nameArr = [];
    let onlineArr = [];
    let avatarArr = [];
    for (let i = 0; i < this.chairCount; ++i) {
        nameArr[i] = null;
        onlineArr[i] = null;
        avatarArr[i] = null;
    }
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            if (user && user.userStatus != roomProto.userStatusEnum.OFFLINE && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
                nameArr[user.chairID] = user.userInfo.nickname;
                onlineArr[user.chairID] = (user.userStatus != roomProto.userStatusEnum.OFFLINE);
                avatarArr[user.chairID] = user.userInfo.avatar;
            }
        }
    }
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];

            if (user && user.userInfo.isRobot) {
                //机器人自动同意
                let times = utils.getRandomNum(1, 10);
                setTimeout(() => {

                    this.askForDismiss(user.userInfo.uid, false);

                }, times * 1000);
            } else if (user && user.userStatus != roomProto.userStatusEnum.OFFLINE && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
                let senddata = roomProto.getAskForDismissPushData(this.askForExitArr, nameArr, null, this.dismissTick, askUser.chairID, onlineArr, avatarArr);
                this.sendRoomData(senddata, [{
                    uid: user.userInfo.uid,
                    sid: user.userInfo.frontendId,
                }]);
                this.showLog(`uid = ${uid} ------------askForDismiss end------------------`);
            }
        }
    }
    let unionData = await dao.findOneData('unionModel', {
        unionID: this.creatorInfo.unionID
    });
    if (isExit == false) {

        if (this.unionRoom && unionData && unionData.dismissType == 1) {
            let playUserCount = 0;
            let unAgreeDismissCount = 0;
            for (let key in this.userArr) {
                if (this.userArr.hasOwnProperty(key)) {
                    let user = this.userArr[key];
                    if (user && user.userStatus != roomProto.userStatusEnum.OFFLINE && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
                        ++playUserCount;
                        if (this.askForExitArr[user.chairID] == false) {
                            ++unAgreeDismissCount;
                        }
                    }
                }
            }
            //不解散
            if (unAgreeDismissCount * 2 >= playUserCount) {
                if (this.answerExitSchedule) {
                    clearInterval(this.answerExitSchedule);
                    this.answerExitSchedule = null;
                }
                this.askForExitArr = null;
                this.showLog(`uid = ${uid} -------------askForDismiss can not dissmiss-----------------`);
                setTimeout(() => {
                    this.sendRoomDataToAll(roomProto.roomDismissResultPush());
                }, 300);
            }
        } else {

            //不能解散
            if (this.answerExitSchedule) {
                clearInterval(this.answerExitSchedule);
                this.answerExitSchedule = null;
            }
            this.askForExitArr = null;
            this.showLog(`uid = ${uid} -------------askForDismiss can not dissmiss-----------------`);
            setTimeout(() => {
                this.sendRoomDataToAll(roomProto.roomDismissResultPush());
            }, 300);
        }
        this.showLog(`uid = ${uid} ------------askForDismiss refuse ------------------`);
    } else if (isExit == true) {
        let playUserCount = 0;
        let agreeDismissCount = 0;
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (user && user.userStatus != roomProto.userStatusEnum.OFFLINE && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
                    ++playUserCount;
                    if (this.askForExitArr[user.chairID]) {
                        ++agreeDismissCount;
                    }
                }
            }
        }
        this.showLog(`uid = ${uid} -----------askForDismiss agree------------------`);
        if (this.unionRoom && unionData && unionData.dismissType == 1) {
            if (agreeDismissCount * 2 > playUserCount) {
                if (this.answerExitSchedule) {
                    clearInterval(this.answerExitSchedule);
                    this.answerExitSchedule = null;
                }
                this.showLog(`uid = ${uid} ------------askForDismiss dissmiss room------------------`);
                await this.dismissRoom(enumeration.gameRoomDismissReason.USER_DISMISS);
            }
        } else {
            if (playUserCount == agreeDismissCount) {
                if (this.answerExitSchedule) {
                    clearInterval(this.answerExitSchedule);
                    this.answerExitSchedule = null;
                }
                this.showLog(`uid = ${uid} ------------askForDismiss dissmiss room------------------`);
                await this.dismissRoom(enumeration.gameRoomDismissReason.USER_DISMISS);
            }
        }

    }
    if (this.askForExitArr) {
        let playUserCount = 0;
        let agreeDismissCount = 0;
        for (let key in this.userArr) {
            if (this.userArr.hasOwnProperty(key)) {
                let user = this.userArr[key];
                if (user && user.userStatus != roomProto.userStatusEnum.OFFLINE && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
                    ++playUserCount;
                    if (this.askForExitArr[user.chairID] != null) {
                        ++agreeDismissCount;
                    }
                }
            }
        }
        if (playUserCount == agreeDismissCount && this.askForExitArr.indexOf(false) != -1) {
            if (this.answerExitSchedule) {
                clearInterval(this.answerExitSchedule);
                this.answerExitSchedule = null;
            }
            this.askForExitArr = null;
            this.showLog(`uid = ${uid} -------------askForDismiss can not dissmiss-----------------`);
            setTimeout(() => {
                this.sendRoomDataToAll(roomProto.roomDismissResultPush());
            }, 300);

        }
    }
};

/*
 * 玩家换座位
 */
pro.userChangeSeat = async function (uid, fromChairID, toChairID) {


    this.showLog(`user watch ask sit down uid = ${uid} fromChairID = ${fromChairID} toChairID = ${toChairID} `);
    //基础条件判断
    if (typeof fromChairID !== 'number' || typeof toChairID !== 'number') return;
    if (fromChairID < 0 || toChairID < 0) return;
    if (fromChairID === toChairID) return;

    //不是旁观玩家不能坐下
    if (toChairID >= this.chairCount || fromChairID < this.chairCount) {
        this.showLog(`玩家座位错误 fromChairID = ${fromChairID} toChairID = ${toChairID} `);
        return;
    }

    //最后三局不能坐下
    if (this.gameRule.lastThreeJoin && fromChairID >= this.chairCount && (this.maxBureau - this.curBureau) <= 3) {
        let userChangeSeatPush = roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, 0, code.THE_FINAL_THREE_MATCHES);
        this.showLog(`last 3 time can not join uid = ${uid} maxBureau = ${this.maxBureau} curBureau = ${JSON.stringify(this.curBureau)} `, true);
        this.sendRoomData(userChangeSeatPush);
        return;
    }

    //玩家信息不正确
    let user = this.userArr[uid];
    if (!user || user.chairID !== fromChairID) {
        this.showLog(`wrong user info fromChairID = ${fromChairID} user = ${JSON.stringify(user)} `, true);
        return;
    }

    // 目标位置如果有人，也不能换座位
    let toUser = this.getUserByChairID(toChairID);
    if (!!toUser) {
        let userChangeSeatPush = roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.userStatus, code.ROOM_SEAT_NOT_EMPTY);
        this.showLog(` this seat is not empty uid = ${toUser.userInfo.uid} toChairID = ${toChairID} message = ${JSON.stringify(userChangeSeatPush)} `, true);
        this.sendRoomData(userChangeSeatPush);
        return;
    }

    let lockKey = this.roomID + '-' + toChairID;
    let isLocked = redisService.isLocked(lockKey, 10);
    //可以进入
    if (!isLocked) {

        let errCode = await this.checkEntryRoom(user.userInfo);
        if (errCode != code.OK) {
            this.sendRoomData(roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.userStatus, errCode));
            this.showLog(`user sit fail uid = ${uid} errCode = ${errCode} fromChairID = ${fromChairID} toChairID = ${toChairID} isLocked = ${isLocked} `);
            redisService.unLock(lockKey);
            return;
        } else {

            let userData = await userDao.getUserData({
                uid: uid
            });
            let newUserInfo = userData.unionInfo.find(function (e) {
                return e.unionID === this.creatorInfo.unionID;
            }.bind(this));
            let score = parseFloat(newUserInfo.score.toFixed(1));
            let toUser = this.getUserByChairID(toChairID);

            if (!!toUser) {
                let userChangeSeatPush = roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.userStatus, code.ROOM_SEAT_NOT_EMPTY);
                this.showLog(`this seat is not empty uid = ${toUser.userInfo.uid} toChairID = ${toChairID}  `, true);
                this.sendRoomData(userChangeSeatPush);

            } else if (score <= 0 || score <= this.gameRule.scoreLowLimit) {
                //获取新的金币
                let userChangeSeatPush = roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.userStatus, code.BE_SHORT_OF_GOLD_COINS);
                this.showLog(`can not sit down uid = ${uid} score = ${score} scoreLowLimit = ${this.gameRule.scoreLowLimit}`, true);
                this.sendRoomData(userChangeSeatPush);

            } else {


                if (this.curBureau < 1 && this.unionRoom) {
                    let unionData = await dao.findOneData('unionModel', {
                        unionID: this.creatorInfo.unionID
                    });
                    if (unionData.unReadyKick) {
                        this.gameRule.unReadyKick = unionData.unReadyKick;
                    }
                    if (this.gameRule.unReadyKick) {
                        this.addReadyKickEvent(user.userInfo.uid, this.gameRule.unReadyKick);
                    }
                }

                this.userArr[uid].userInfo.score = score;
                this.userArr[uid].chairID = toChairID;
                this.userArr[uid].userStatus = roomProto.userStatusEnum.NONE;
                this.showLog(`user sit down success fromChairID = ${fromChairID} toChairID = ${toChairID}`);
                this.sendRoomData(roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.userStatus, code.OK));
            }
            redisService.unLock(lockKey);
            return;
        }

    } else {
        //此房间当前位置被锁定了 提示房间已满
        this.showLog(`this seat locked uid = ${uid} toChairID = ${toChairID} `, true);
        this.sendRoomData(roomProto.getUserChangeSeatPush(fromChairID, toChairID, uid, user.userStatus, code.ROOM_JOIN_BUSY_LIMIT));
        return;
    }

};

/*
 * 玩家聊天
 */
pro.userChat = function (uid, type, msg, recipientID) {
    let user = this.userArr[uid];
    if (!user) {
        return;
    }
    let chairID = user.chairID;
    this.sendRoomData(roomProto.userChatPush(chairID, uid, type, msg, recipientID));
};

/*
 * 赠送礼物
 */
pro.userSendGift = function (uid, toChairID, giftID) {
    let user = this.userArr[uid];
    if (!user) {
        return;
    }
    if (toChairID == user.chairID) {
        return;
    }
    let fromChairID = user.chairID;
    let lockKey = fromChairID + '-Join-Room';
    let isLocked = redisService.isLocked(lockKey, 3);
    if (!isLocked) {
        this.sendRoomData(roomProto.getUserGiftSendPush(fromChairID, toChairID, giftID));
    }
};

// ---------------------------------离开房间相关----------------------------------


//用户再来一局离开原房间
pro.userLeaveRoomByAgain = async function (uid) {

    if (!this.gameFrameSink) {
        logger.info(`[roomFrame][${this.roomID}]userLeaveRoomByAgain begin uid: ` + uid + "this.gameFrameSink is null");
        return;
    }
    logger.info(`[roomFrame][${this.roomID}]userLeaveRoomByAgain begin uid: ` + uid);
    let user = this.userArr[uid] || null;
    if (user) {
        await userDao.updateUserDataByUid(user.userInfo.uid, {
            roomID: ""
        });
        if (user.userInfo.frontendId) {
            let session = await pomeloServices.getSession(user.userInfo.frontendId, user.userInfo.uid);
            if (session) {
                await pomeloServices.pushSessionData(session, "roomID", null);
            }
        }
        this.gameFrameSink.onEventUserOffLine(user.chairID);

        //关闭弹出游戏
        if (this.kickSchedules[uid]) {
            clearTimeout(this.kickSchedules[uid]);
            delete this.kickSchedules[uid];
        }
        delete this.userArr[uid];
        --this.currentUserCount;
        // 判断房间是否解散
        if (this.efficacyDismissRoom()) {
            logger.info("[roomFrame] userLeaveRoomByAgain  dismissRoom uid:" + uid);
            await this.dismissRoom(enumeration.gameRoomDismissReason.NONE);
        }
        logger.info(`[roomFrame][${this.roomID}]userLeaveRoomByAgain End uid: ` + uid);
    }


}

pro.userAskLeaveRoom = function (uid, isMaster = false) {

    this.showLog(`user ask for leave room uid = ${uid} isMaster = ${isMaster}`);

    let user = this.userArr[uid] || null;
    if (!user) {
        this.showLog(`user leave fail uid = ${uid} reason = not in this room `);
        return code.CAN_NOT_LEAVE_ROOM;
    }

    this.isCanStart = false;
    //游戏未开始 可以离开 游戏开始了 只有旁观可以离开
    if ((!this.isCanLeave || this.gameStarted) && user.userStatus != roomProto.userStatusEnum.WATCH && user.chairID < this.chairCount) {
        logger.info("roomFrame", "userAskLeaveRoom uid fail");
        if (!isMaster) {
            this.sendPopDialogContent(code.CAN_NOT_LEAVE_ROOM, [user.chairID]);
        }
        let response = roomProto.userLeaveRoomResponse(user.chairID, uid);
        this.sendRoomDataToAll(response);
        this.showLog(`user leave fail  uid = ${uid} chairID = ${user.chairID} userStatus = ${user.userStatus} isCanLeave = ${this.isCanLeave}  gameStarted = ${this.gameStarted} `, true);
        this.isCanStart = true;
        return code.CAN_NOT_LEAVE_ROOM;
    } else {
        this.showLog(`user leave success uid = ${uid} chairID = ${user.chairID} userStatus = ${user.userStatus}  isCanLeave = ${this.isCanLeave} gameStart = ${this.gameStarted} `);
        let curIndex = this.readyList.findIndex(item => item == uid);
        if (curIndex != -1) {
            this.readyList.splice(curIndex, 1);
        }
        this.userLeaveRoom(uid).catch((err) => {
            logger.error(new Error(err.stack));
        });
        return code.OK;
    }

};

pro.userLeaveRoom = async function (uid, iskickUser = true) {


    this.showLog(`user begin leave room uid = ${uid} `);
    let user = this.userArr[uid] || null;
    if (!user || !this.gameFrameSink) {
        this.showLog(`user leave fail  uid = ${uid} reason = not in this room `);
        this.isCanStart = true;
        return;
    }

    this.sendRoomDataToAll(roomProto.userLeaveRoomResponse(user.chairID, uid));

    if (this.gameStarted && (user.userStatus & roomProto.userStatusEnum.PLAYING) !== 0) {
        if (this.gameFrameSink && this.gameFrameSink.isUserEnableLeave(user.chairID)) {
            await this.kickUser(uid, iskickUser);
        } else {
            user.userStatus |= roomProto.userStatusEnum.OFFLINE;
            if (this.roomType !== enumeration.roomType.HUNDRED) {
                this.sendRoomDataToAll(roomProto.userOffLinePush(user.chairID));
            }
            this.gameFrameSink.onEventUserOffLine(user.chairID);
        }
    } else {
        await this.kickUser(uid, iskickUser);
    }
    //退出后可以开始开始游戏
    this.isCanStart = true;
    if (this.efficacyStartGame()) {
        this.startGame().catch(e => {
            logger.error(e.stack)
        });
    }
    // 判断房间是否解散
    if (this.efficacyDismissRoom()) {
        await this.dismissRoom(enumeration.gameRoomDismissReason.NONE);
    }
    let curUser = this.userArr[uid] || null;
    this.showLog(`user leave room end uid = ${uid} curUser = ${curUser} currentUserCount = ${this.currentUserCount}`);
};

pro.userLeaveRoomNotify = async function (uidAndSidArr, roomID) {
    for (let i = 0; i < uidAndSidArr.length; ++i) {

        let uidAndSid = uidAndSidArr[i];
        let curUser = await userDao.getUserDataByUid(uidAndSid.uid);
        //20241101信息一致才清除
        if (curUser && curUser.roomID && curUser.roomID.toString() == roomID.toString()) {
            let updateUserData = { roomID: "" };
            // this.showLog(`清除玩家房间信息开始 user=${JSON.stringify(uidAndSid)}`);
            await userInfoServices.updateUserDataNotify(uidAndSid.uid, uidAndSid.sid, { roomID: "" }).catch((err) => { logger.error(err.stack) });
            await userDao.updateUserDataByUid(uidAndSid.uid, updateUserData);
            if (!uidAndSid.sid) continue;
            let session = await pomeloServices.getSession(uidAndSid.sid, uidAndSid.uid);
            if (!session) continue;
            await pomeloServices.pushSessionData(session, "roomID", null);
            this.showLog(`clean user status end uid = ${uidAndSid.uid} sid = ${uidAndSid.sid}`);
            // logger.error(`[${ this.getLogTitle() }]更新用户离开状态结束`);
        }
    }
};

pro.kickUser = async function (uid, iskickUser = true) {


    this.showLog(`kick user and update status uid = ${uid} currentUserCount = ${this.currentUserCount}`);
    let user = this.userArr[uid] || null;
    if (!!user) {
        // 通知游戏，用户离开
        if (this.gameStarted) this.gameFrameSink.onEventUserLeave(user.chairID);

        let userRoomInfo = {
            userInfo: user.userInfo,
            chairID: user.chairID,
            roomID: this.roomID,
            uid: uid,
        };
        let otherUserLeavePush = roomProto.userLeaveRoomPush(userRoomInfo);
        //20230605 判断玩家是否在当前房间
        let curUser = await userDao.getUserDataByUid(uid);
        //在当前房间中
        if (curUser.roomID && curUser.roomID.toString() == this.roomID.toString()) {

            this.showLog(`user in this room uid = ${uid}  userRoomID = ${curUser.roomID} currentUserCount = ${this.currentUserCount}`);
            // 玩家离开房间，修改玩家信息
            await this.userLeaveRoomNotify([{
                uid: uid,
                sid: user.userInfo.frontendId
            }], this.roomID).catch(err => {
                logger.error(err.stack)
            });
            if (iskickUser) {
                this.sendRoomDataToAll(otherUserLeavePush);
            } else {
                this.sendRoomDataExceptUid(otherUserLeavePush, [uid]);
            }
        } else {
            this.showLog(`kick user not in this room uid = ${uid}  userRoomID = ${curUser.roomID} currentUserCount = ${this.currentUserCount}`, true);
            this.sendRoomDataExceptUid(otherUserLeavePush, [uid]);
        }

        delete this.userArr[uid];
        delete this.isCanNotReady[uid];
        --this.currentUserCount;
        // 停止定时器
        if (!!this.offlineSchedulerIDs[uid] || this.offlineSchedulerIDs[uid] === 0) {
            scheduler.cancelJob(this.offlineSchedulerIDs[uid]);
            delete this.offlineSchedulerIDs[uid];
        }
        // 判断启动定时器
        if (!!this.startSchedulerID && !this.isShouldSchedulerStart()) {
            clearInterval(this.startSchedulerID);
            this.startSchedulerID = null;
        }
        this.showLog(`kick user end update user status uid = ${uid} currentUserCount = ${this.currentUserCount}`);
    }
};

pro.userOffLine = async function (uid) {


    let user = this.userArr[uid];
    if (!user) return;

    this.showLog(`user offline uid = ${uid} chairID = ${user.chairID}`);

    if (!user) {
        this.showLog(`user offline fail uid = ${uid} reason = not in room`, true);
        return;
    }
    // if ((!this.gameStarted && this.curBureau === 0) || (user.userStatus & roomProto.userStatusEnum.DISMISS) == 0) { 
    //没参与游戏的玩家 执行离开房间操作
    // if ((user.chairID >= this.chairCount) || (!this.gameStarted && this.curBureau === 0)) {

    //     if (user.chairID < this.chairCount) {
    //         this.isCanStart = false;
    //     }
    //     this.showLog(`user offline leave room uid = ${uid} chairID = ${user.chairID} gameStarted = ${this.gameStarted} isCanStart = ${this.isCanStart}`);
    //     await this.userLeaveRoom(uid);
    // } else 
    {
        //更改状态为离线状态
        let WATCH = true;
        if (this.userArr[uid].userStatus == roomProto.userStatusEnum.WATCH) {
            WATCH = false;
        }
        this.userArr[uid].userStatus = roomProto.userStatusEnum.OFFLINE;
        this.showLog(`update status to offline uid = ${uid} chairID = ${user.chairID} gameStarted = ${this.gameStarted} userStatus = ${this.userArr[uid].userStatus}`);
        this.sendRoomDataToAll(roomProto.userOffLinePush(user.chairID));
        if ((user.chairID < this.chairCount) || (this.gameStarted && this.curBureau > 0)) {

            if (WATCH == true) {
                this.userArr[uid].userStatus = roomProto.userStatusEnum.PLAYING;
                this.showLog(`update status to playing uid = ${uid} chairID = ${user.chairID} gameStarted = ${this.gameStarted} userStatus = ${this.userArr[uid].userStatus}`);
            }
        }
    }
};

// ---------------------------------解散房间相关----------------------------------
pro.efficacyDismissRoom = function () {
    if (this.roomDismissed) return false;
    let userCount = utils.getLength(this.userArr);
    return userCount <= 0 && this.currentUserCount <= 0;
};


pro.getResultDiamond = function () {

    if (this.gameRule.payType == enumeration.roomPayType.AAZHIFU) {
        return this.gameRule.bureau;
    } else {
        return gameConfig.oneUserDiamondCount(this.gameRule.bureau, this.gameRule.gameType, this.chairCount);
    }
}

pro.dismissRoom = async function (reason) {

    this.isGameEnd = true;
    if (this.roomDismissed) return; // 防止重复解散
    this.showLog(`------- dissmiss roon begin Reason = ${reason} ------------`);

    this.roomDismissed = true;
    this.isCanLeave = true;
    this.isCanStart = false;
    if (!!this.startSchedulerID) {
        clearInterval(this.startSchedulerID);
        this.startSchedulerID = null;
    }

    for (let key in this.kickSchedules) {
        clearTimeout(this.kickSchedules[key]);
        delete this.kickSchedules[key];
    }

    try {
        // 获取并存储游戏数据
        await this.recordAllDrawResult();
    } catch (e) {
        logger.error(e.stack);
    }
    //清理机器人信息
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            if (user.userInfo.isRobot) {
                //延迟2秒退出房间 等房间结算
                setTimeout(async () => {
                    // await userDao.updateUserData({ 'uid': user.userInfo.uid }, { roomID: "" });
                    this.userLeaveRoom(user.userInfo.uid).catch((err) => {
                        logger.error(new Error(err.stack));
                    });
                }, 2000);
            }
        }
    }

    let uidAndSidArr = [];
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            uidAndSidArr.push({
                uid: user.userInfo.uid,
                sid: user.userInfo.frontendId
            });
        }
    }
    // this.showLog(`游戏解散房间流程2 userList = ${JSON.stringify(uidAndSidArr)}`);
    await this.userLeaveRoomNotify(uidAndSidArr, this.roomID).catch((err) => {
        logger.error(err.stack);
    });
    pomelo.app.unionManager.dismissRoom(this.roomID);
    this.destroyRoom(reason);
    this.sendRoomDataToAll(roomProto.roomDismissPush(reason));

    this.showLog(`------- dissmiss room end ------------`);

};

pro.destroyRoom = function (reason) {
    if (this.answerExitSchedule) {
        scheduler.cancelJob(this.answerExitSchedule);
    }
    // logger.error("roomFrame", "destroyRoom roomID:" + this.roomID);
    this.showLog(`destroy room reason = ${reason} `);
    this.gameFrameSink.onEventRoomDismiss(reason);
    this.gameFrameSink = null;
};

pro.ownUser = function (uid) {
    let userArr = this.userArr;
    for (let key in userArr) {
        if (userArr.hasOwnProperty(key) && (userArr[key].userInfo.uid === uid)) {
            return true;
        }
    }
    return false;
};

// ---------------------------------房间接口相关----------------------------------
pro.getUserByChairID = function (chairID) {
    let userArr = this.userArr;
    for (let key in userArr) {
        if (userArr.hasOwnProperty(key) && (userArr[key].chairID === chairID)) {
            return userArr[key];
        }
    }
    return null;
};
//通过uid查找用户
pro.getUserByUID = function (uid) {
    let user = this.userArr[uid];
    if (user) {
        return this.userArr[uid];
    }
    return null;
}
//是否未联盟创建的比赛场
pro.isCreateByUnion = function (creatorInfo) {
    return creatorInfo.creatorType == enumeration.roomCreatorType.UNION_CREATE;
}

pro.getCurrentUserCount = function () {
    return this.currentUserCount;
};

pro.isShouldDelete = function (time) {
    return (Date.now() - this.lastNativeTime >= time);
};

pro.getRoomInfo = function () {
    if (this.roomDismissed) return null;
    let roomUserInfoArr = [];
    for (let key in this.userArr) {
        if (this.userArr.hasOwnProperty(key)) {
            let user = this.userArr[key];
            // if (user.chairID >= this.chairCount) continue;
            roomUserInfoArr.push({
                avatar: user.userInfo.avatar,
                uid: user.userInfo.uid,
                chairID: user.chairID,
                nickname: user.userInfo.nickname,
                winScore: user.winScore || 0
            });
        }
    }
    return {
        roomID: this.roomID,
        gameRule: this.gameRule,
        gameStarted: this.hasStartedOneBureau,
        isGameEnd: this.isGameEnd,
        maxBureau: this.maxBureau,
        curBureau: this.curBureau,
        gameStatus: this.gameStarted,
        roomUserInfoArr: roomUserInfoArr
    }
};

pro.isUserInRoom = function (uid) {
    if (this.roomDismissed) return false;
    return !!this.userArr[uid];
};

/* 正在解散中 */
pro.isDismissing = function () {
    return !!this.askForExitArr;
};

//延迟时间
pro.delay = async function (time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, time);
    });
}

pro.addReadyKickEvent = async function (uid, tick) {

    if (this.hasStartedOneBureau) return;
    if (this.autoReadyTimer[uid]) {
        clearTimeout(this.autoReadyTimer[uid]);
        delete this.autoReadyTimer[uid];
    }
    this.autoReadyTimer[uid] = setTimeout(async () => {
        if (this.autoReadyTimer[uid]) {
            delete this.autoReadyTimer[uid];
        }
        let user = this.userArr[uid];
        if (!this.hasStartedOneBureau && user && ((user.userStatus & roomProto.userStatusEnum.READY) === 0)) {

            //玩家超时踢出去后  不能准备了
            this.isCanNotReady[uid] = true;
            await this.kickUser(uid).catch(e => {
                logger.error(e.stack)
            });

            if (this.efficacyStartGame()) {
                this.startGame().catch(e => {
                    logger.error(e.stack)
                });
            }
            // 判断房间是否解散
            if (this.efficacyDismissRoom()) {
                await this.dismissRoom(enumeration.gameRoomDismissReason.NONE);
            }
        }
    }, tick * 1000);
};

pro.addKickScheduleEvent = function (uid, tisk) {

    if (this.hasStartedOneBureau) return;
    if (this.kickSchedules[uid]) {
        clearTimeout(this.kickSchedules[uid]);
        delete this.kickSchedules[uid];
    }
    this.kickSchedules[uid] = setTimeout(async () => {
        if (this.kickSchedules[uid]) {
            delete this.kickSchedules[uid];
        }
        let user = this.userArr[uid];
        if (!this.hasStartedOneBureau && user && ((user.userStatus & roomProto.userStatusEnum.READY) === 0)) {
            await this.kickUser(uid).catch(e => {
                logger.error(e.stack)
            });

            if (this.efficacyStartGame()) {
                this.startGame().catch(e => {
                    logger.error(e.stack)
                });
            }
            // 判断房间是否解散
            if (this.efficacyDismissRoom()) {
                await this.dismissRoom(enumeration.gameRoomDismissReason.NONE);
            }
        }
    }, tisk * 1000);
};

pro.createHongBaoList = function () {

    if (!this.resultLotteryInfo) return;
    let status = !!this.resultLotteryInfo && !!this.resultLotteryInfo.status;
    let arr = [];
    let countArr = (!!this.resultLotteryInfo.countArr && this.resultLotteryInfo.countArr.length === 6) ? this.resultLotteryInfo.countArr : [1, 2, 8, 18, 88, 888];
    let rateArr = (!!this.resultLotteryInfo.rateArr && this.resultLotteryInfo.rateArr.length === 6) ? this.resultLotteryInfo.rateArr : [0.34, 0.60, 0.05, 0.009, 0.001, 0];

    for (let i = 0; i < 10; ++i) {
        if (!status) {
            arr.push(-1);
            continue;
        }
        let user = this.getUserByChairID(i);
        if (!user) {
            arr.push(-1);
            continue;
        }
        let uid = user.userInfo.uid;
        if (this.userJoinGameBureau[uid] > 0 && this.userJoinGameBureau[uid] >= this.gameRule.bureau - 3) {
            let count = countArr[0];
            let rand = Math.random();
            for (let j = 0; j < rateArr.length; ++j) {
                if (rand < rateArr[j]) {
                    count = countArr[j];
                    break;
                } else {
                    rand -= rateArr[j];
                }
            }
            arr.push(count);
        } else {
            arr.push(-1);
        }
    }
    this.userGetHongBaoCountArr = arr;
};

pro.getHongBaoList = function () {
    return this.userGetHongBaoCountArr;
};

pro.updateLotteryInfo = function (resultLotteryInfo) {
    this.resultLotteryInfo = resultLotteryInfo;
};


pro.getLogTitle = function () {
    let title = `[${this.roomID}][${this.gameType}]--[${this.curBureau}][${this.maxBureau}]`;
    return title;
}


//获取指定卡牌
pro.getExceptCards = function (allTestCards, allCards) {

    allCards = allCards.slice();
    let testCards = [];
    for (let key in allTestCards) {
        if (allTestCards[key] && allTestCards[key].length > 0) {
            testCards = testCards.concat(allTestCards[key]);
        }
    }
    for (let i = 0; i < testCards.length; i++) {
        let cards = testCards[i];
        let cardIndex = allCards.findIndex(item => item == cards);
        allCards.splice(cardIndex, 1);
    }
    return allCards.slice();
}


//判断牌是否在当前的牌库中
pro.isInCardList = function (curData, allData) {

    curData = curData.slice();
    allData = allData.slice();
    let indexList = [];
    for (let i = 0; i < curData.length; i++) {
        let curIndex = allData.findIndex(item => item == curData[i]);
        if (curIndex >= 0) {
            indexList.push(curIndex);
            allData.splice(curIndex, 1);
        }
    }
    let isSuccess = false;
    if (indexList.length == curData.length) {
        isSuccess = true;
    }
    return isSuccess;
}


//通过名称获取logger
pro.getLoggerByName = function (name) {

    let logger = require('pomelo-logger').getLogger(name);
    if (!logger) {
        logger = require('pomelo-logger').getLogger('room');
    }
    return logger;
}

pro.showLog = function (message, isError = false) {

    let title = `[${this.roomID}][${this.curBureau}]`;
    let logger = logServices.getLogByType(this.gameType);
    if (isError) {
        logger.error(`${title} ${message}`);
    } else {
        logger.info(`${title} ${message}`);
    }

}